This document describes the ODF build tool. As a typical ODF developer you don’t need to read this document unless you are interested in how this tool works.
Table of Contents
-----------------
• Introduction
• Overview
• Users Guide
• FWTool Reference
• Known Limitations and Future Directions
Introduction
In traditional development environments, applications or systems composed of many sources are typically built using some sort of “Make” tool. Make was first developed for Unix but soon found its way to other operating systems and development environments including MPW on the Macintosh and many development environments for DOS/Windows. Project-oriented environments (such as Think and Code Warrior), which attempt to eliminate the need for Make, are now common on all platforms that ODF currently supports, but these environments are not yet flexible enough to support special needs like Interface Definition Language (IDL) compilers (e.g. SOM). And of course, developers who prefer to use traditional development environments such as MPW need full support for Make-based builds. For these reasons, ODF provides support for Make-based builds of ODF and ODF parts.
It is possible to build large systems using Make. With a naive approach, one can build a large and complex system using only a single makefile, but the makefile will have to be large and complex, and therefore hard to maintain as the system evolves. Many approaches have been used to get around this problem. One approach is to identify components in the system, create a makefile for each component, and then produce a script to build all of the components. This last step of producing a system build script is sometimes done with Make itself.
FWBuild is a build system which attempts to formalize building systems that are composed of components. It provides a means of “factoring” a large and complex makefile into several (or many) small, easy to understand makefiles, and then further parameterizes the build system so that different configurations of the same system can be easily built.
This document is a user guide and reference for the FWBuild system as used to build ODF and ODF parts. The document is composed of this introduction, an Overview section that provides an overview to FWBuild by comparing and contrasting it to traditional builds that use Make directly, a Users Guide section which provides a more complete description of FWBuild, a Reference Guide section to the tool FWTool, and finally a Known Limitations and Future Directions section.
Overview
Consider Figure 1:
Figure 1
In a traditional build using make, a single make file is used to describe the elements and build rules of a system. Make reads the makefile and processes the file to create a build script which is then executed. Makefiles are often created using references to global environment variables. The output of the build is one or more build targets such as applications or libraries. (Of course, any complex build will also produce many intermediate targets such as individual object files, but for now we will focus on the final build targets.)
Building a system using FWBuild is similar to a traditional build. Multiple makefiles are used. These makefiles are “factored” or “modular” makefiles, each specifying only some local aspect of the build system. Some of these makefiles apply to the entire system, others apply only to specific components. Some makefiles contain only definitions of system elements and dependencies (e.g. the list of C++object files that make up a shared library, and the dependencies these objects have on specific C++ header files), others contain only rules that describe how to translate from one kind of element (e.g. a C++ source file) into another kind of element (e.g. a C++ object file). In addition to these factored makefiles, FWBuild uses command-line options and other system-wide configuration files to determine which build configuration will be built (e.g. CFM-68K or CFM-PPC, debug or non-debug, etc.) FWBuild uses these inputs to generate a script that will execute Make one or more times. In addition, FWBuild arranges to provide definitions for certain environment variables, and these definitions may change between each invocation of Make.
Users Guide
To use FWBuild, invoke the FWBuild command from the command line or a script. FWBuild is a script which invokes the tool FWTool. The FWBuild script is quite simple; it may be useful to show it here:
SET Echo 0
SET Exit 0
Execute FWSetBuildPaths
SET FWTemp "{FWTempDir}FWBuild.tmp"
FWTool {"Parameters"} -o "{FWTemp}"
SET XExitStatus {Status}
IF ¬"{XExitStatus}"
IF "`EXISTS "{FWTemp}"`"
Execute "{FWTemp}"
END
ELSE
SET Exit 1
{FWFailed}
END
SET Exit 1
Exit {XExitStatus}
As you can see, FWBuild simply passes all command line parameters to an MPW shell tool FWTool, and arranges for the output of FWTool to be placed in a temporary file "{FWTempDir}FWBuild.tmp". It then checks the exit status returned from FWTool, and if no errors were reported, executes the output of the tool. You will also notice that prior to calling FWTool that FWBuild invokes another MPW script, FWSetBuildPaths. This script is used to setup the value of certain MPW shell variables that are used by FWTool internally to determine where it should place the objects it builds. It should be clear that the capabilities and features of FWBuild are actually determined by the tool, FWTool. However, it is usually convenient to consider FWBuild and FWTool to be a single unit, which we refer to as FWBuild. In the remainder of this document we will continue to use the term FWBuild to refer to the build system as a whole, but we will use the term FWTool when we are discussing details of the MPW tool itself.
NOTE: FWTool is actually portable code that runs as a shell tool under MPW, or as a simple application under DOS. There are subtle differences to using FWBuild under DOS, but in order to simplify discussion we defer describing those differences to an appendix.
A simple invocation of FWBuild builds a single component. When building a single component, it is generally necessary to first set the working directory to the directory containing the component. Building the component might then be accomplished by simply issuing the command
FWBuild <component>
where <component> is the name of the component to be built. This command will build a default configuration of the component. Various command line options can be added to this command to specify details of the build configuration or to otherwise alter the build process in useful ways.
Build Suites
The FWBuild system uses the concept of “Build Suites”. A build suite is a collection of tools and parameters used to create a build. Build suites are typically used to switch between tool sets provided by different vendors. For example, three vendors provide C++ compilers that work with MPW: Apple, Symantec, and Metrowerks. Each of these vendors uses different compilers for 68K and PowerPC code generation. This results in at least six choices for building C++ systems for the Macintosh. Furthermore, on the 68K it is necessary to choose between the classic runtime model or the code fragment runtime model, which requires additional choices in the tool sets used to build the system.
At the simplest level, a build suite is simply a collection of specific tools. For example, a Symantec suite might use Symantec’s compiler SCpp and Apple’s linker Link (Symantec doesn’t provide a linker for MPW). Additional tools like Apple’s Rez and ODFRC might also be specified as part of the Symantec suite.
BldSuite.txt
Build suites are defined in the file BldSuite.txt. The following is a portion of BldSuite.txt defining one build suite:
[Suite] SymantecCFM
[Tag] CF
[EchoTool] MPWEcho
[MakeTool] MPWMake
[Assembler] MPWAssembler
[CCompiler] SymantecCCompiler
[CppCompiler] SymantecCppCompiler
[Linker] MPWILink
[Librarian] MPWLib
[ODFRC] MPWODFRC
[ResourceCompiler] MPWResourceCompiler68K
These lines of text define a suite named “SymantecCFM”. The suite is given a two letter tag “CF” which appears in build target directory names (see below). Various tools are specified as belonging to the suite. Each tool is specified indirectly through a tool label (e.g. “SymantecCppCompiler”); the tool label must appear as a tool definition in the file BldTools.txt (see below).
BldSuite.txt is global to all components and systems built with FWBuild. FWTool finds it via the path “{FWToolsDir}BldSuite.txt”. For ODF, FWToolsDir is normally defined to be “{ODF}Tools:Mac:”.
BldTools.txt
Build tools are defined in the file BldTools.txt. The following is a portion of BldTools.txt:
[CppCompiler] MPWMrCpp
[Location] MrCpp
[Defaults] -w 17 -w 6 -w 25 -w 29
[Debug] -D FW_DEBUG
[Include]
[Lib]
[Linker] MPWPPCLink
[Location] ppclink -outputformat pef
[Defaults] -mf -sym big -warn
These lines of text define two tools, a C++ compiler labeled “MPWMrCpp”, and a linker labeled “MPWPPCLink”. A build suite could be defined to use these two tools if it included these two definitions:
[CppCompiler] MPWMrCpp
[Linker] MPWPPCLink
BldTools.txt, like BldSuite.txt, is global to all components and systems built by FWBuild, and is located by FWTool via the path “{FWToolsDir}BldTools.txt”.
Build Target Directories
This ability to specify different build suites is crucial for building libraries (such as ODF) which are intended to work with tools provided by multiple vendors. To ensure compatibility across multiple vendor’s development environments it is necessary to regularly build and test the library with each environment while the library is being developed. The ODF team has found that it is absolutely necessary for each engineer to be able to build and test multiple build configurations on the same machine. Since FWBuild supports choosing a build suite with a single command line option, it is easy for an engineer to build different configurations using familiar build commands.
One potential problem with building several configurations on one machine is keeping separate the intermediate (and final!) build targets across configurations. There are two different approaches that are used by FWBuild to solve this problem. Both methods separate build targets into configuration-specific target directories. The method originally used by FWBuild automaticallily segregated targets by generating target directories with unique encoded names within the target. (If you are familiar with MacApp's MABuild tool, then you will understand how this method works.) A second approach would place each compiler's binaries into pre-existant folders which we have named and placed at a location convienent for our use. We presently use this second approach in order to overcome some difficulties in using multi-vendor IDE systems. The problem with using the first approach is that if you use two different IDE compilers, such as Metrowerks CodeWarrior and Symantec C++, the IDE projects will see each others intermediate binaries since the share the same folder hierarchy. If we instead separate their folder hierarchy so that they are completely independent of one another the problem disappears. If you are using only MPW to build your ODF parts you could revise your build system to use the original method by editing the .bmk files (described later) and passing to FWBuild the "-Separate" command line option.
FWBuild typically expects three different subdirectories to pre-exist in the target. A :Bin: folder to hold the final built target, a :Lib: folder to hold static libraries built by a previous step that will be linked into the built target, and finally a :Obj: folder to hold the object files created by a compiler before the linked together as a static library. In some cases, one or more of these folders will not be required. Here is a portion of a sample directory layout:
Figure 2
In this sample directory layout, there are two directories for each compiler environment, a debug version and a release version. The first two letters tell you which environment the folder is for. CW is for Metrowerks CodeWarrior, MC is for MPW's MrC compiler environment, SC is for MPW's SCpp compiler environment, and finally RB is for the Symantec C++ (Rainbow) environment. After the initial first two letters is either '68K' or 'PPC' depending on whether or not the target build is for a Motorola 68000 or PowerPC architecture. There is only one exception to this folder naming scheme. The ODF Shared Library is built into a folder that starts with the prefix SL. Unless you choose to to alter ODF source code, you will not have to build the various versions of the ODF libraries. They are pre-built for you.
NOTE: In ODF Release 2 we have discontinued, for now, support for the Symantec C++ (Rainbow) development system.
FWBuild Makefiles
As mentioned earlier, FWBuild uses a modular approach that results in several (or perhaps many) small makefiles instead of one monolithic makefile. The exact number of makefiles that will be used to build a specific component is not fixed since FWTool provides command line options that allow a developer to specify additional make files. In the baseline case, four makefiles are used. These are effectively concatenated into one makefile, which is then processed by make. The four makefiles are, in order: {FWToolsDir}MacMake.dfn, MacMake.bmk, MacMake.dep, {FWToolsDir}MacMake.rul. The first file, {FWToolsDir}MacMake.dfn, provides global definitions that are common to all components in the system under development. Likewise, the last file, {FWToolsDir}MacMake.rul, provides global translation rules that are common to all components. The other two files are specific to the component being built.
Each component should have a MacMake.bmk and a MacMake.dep. The MacMake.bmk file should define the primary build target for the component, and then define the include files, component object files, resources, etc. that are directly used to create the component’s primary build target. Consider as an example the MacMake.bmk file used to build the example part ODFNothing. The file is shown below, with some lines omitted for brevity:
This file provides definitions for several symbols: __ComponentName, __ComponentSignature, __IncludeDirs, __IDLIncludeDirs, __ComponentSourcesFromIDL, __FWRCIncludeDirs, __ComponentObjects, __FrameworkObjects, etc. The first symbol names the item that will be build for this component: either a CFM component (i.e. shared library), or all files required by IDEs such as CodeWarrior. The second symbol defines the file signature of the shared library.
The remaining symbols all define elements used to create the build. The elements are typically lists, e.g. a list of include paths, or a list of object files.
Build Targets (Macintosh)
As noted above, __ComponentName determines the overall target of the makefile. When you issue an FWBuild command, you will need to specify how MPW should build the target.
When using an MPW-hosted compiler like SCpp (or MrC), you will need MPW to build every part of the project (resource files, idl files, and source files). However, when using the Metrowerks or Symantec IDEs, you don’t want MPW to compile the .cpp files, only the Rez and IDL files. You use extensions to tell MPW what portion of the project to build. Since Metrowerks environment is now provided with plug-in modules to compile .r and .fr files you no longer need MPW to compile them. (Although you are free to do so if you wish.) Only the SOM compiler must be used prior to executing your Metrowerks IDE.
You put the extension on the command line after the target name. For example, the __ComponentName for the Hello part is just Hello, but on the FWBuild command line you would enter Hello.IDE or Hello.CFM.
{ComponentName}.CFM
The CFM target causes FWBuild to use MPW to compile resources, idl, and code. You will use this target when using the MPW-hosted MrC and SCpp compilers. The result is then linked into a shared library called {ComponentName}. For example, use “FWBuild ODFNothing.CFM” to build Nothing using the MrC compiler. If you do .CFM builds then remember to set __ComponentSignature so the shared library is assigned the correct file creator.
{ComponentName}.IDE
This target causes FWBuild to compile only resources and IDL files. You will use this targetwhen compiling with the Metrowerks or Symantec IDEs. (Note: Symantec can compile Rez files but cannot yet compile ODFRC files intenally). For example, use “FWBuild -MetroWerks ODFNothing.IDE” to compile Nothing’s resource and IDL files for Metrowerks. You can then switch to the MetroWerks environment and do your remaining development there.
{ComponentName}.RSED
This target causes FWBuild to compile only resource files (both Rez and ODFRC). It’s invoked automatically by {ComponentName}.IDE.
{ComponentName}.SOM
This target causes FWBuild to compile only IDL files using the som Compiler. It’s invoked automatically by {ComponentName}.IDE.
{ComponentName}.LIB
This target causes FWBuild to compile all sources as with .CFM (using MPW), but to create a statically linked library instead of a shared library. You won’t be likely to use this for your projects since OpenDoc uses shared libraries. However, portions of ODF are built this way so they can be linked into your project directly (the ODFCFMInit function, for example).
Autobuild and Autotarg.txt
One feature of FWBuild deserves special mention here. FWBuild is designed to simplify building systems that are designed as a set of components. A typical build using FWBuild will build a single target for a single component. For example, the following MPW shell commands build the ODF example part ODFNothing:
The first command sets the directory to the directory containing the MacMake.bmk file for the ODFNothing component. The second command runs FWBuild to build the target “ODFNothing”. The MacMake.bmk for Nothing defines a __ComponentName target named ODFNothing, and the FWBuild target was “ODFNothing.CFM”, so FWBuild will use the build rules for CFM components to build a CFM shared library for the Nothing component.
For Nothing to be built, it will need to be linked against other libraries (static and shared) that comprise the OpenDoc Development Framework. If these libraries haven’t been built, the build of ODFNothing will fail. To address this problem, FWBuild supports a command-line option -Autobuild. Adding the -Autobuild option to an FWBuild command will cause FWBuild to update every component in a special list of components. This list is defined in the file {FWToolsDir}Autotarg.txt. Currently, this file defines all of the parts that make up ODF (but not the examples). We hope to extend the Autobuild mechanism to make it more flexible; see the section “Known Limitations and Future Directions” below.